﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Diagnostics;
using System.Reflection;
using System.Drawing;
using System.Threading;
using System.Net;
using System.IO;

#if WPF
using Silverlight.Hosting.Wpf;
using Silverlight.Hosting.Wpf.Interop;
using Silverlight.Hosting.Private;
using EventArgs = System.Windows.RoutedEventArgs;
using Color = System.Windows.Media.Color;

namespace Silverlight.Hosting.Wpf.Private
#else
using Silverlight.Hosting.Interop;

namespace Silverlight.Hosting.Private
#endif
{
    public abstract class XcpContainer : AxHostContainer, IHostContainerServicesProvider, IXcpControlHost, IXcpControlHost2, IXcpControlHost3, Interop.IServiceProvider, IPropertyBag, IOleClientSite
    {
        protected internal object _unkControl;
        protected Settings _settings;
        protected Content _content;
        private bool _created;
        private Uri _baseUrl;
        private string _xaml;
        private XcpHostOptions _hostOptions;
        private Version _version;
        private Dictionary<string, object> _config;

#if !NO_XAML_SOURCE_FIX
        private string _tempXamlFile;
#endif


        public XcpContainer(bool windowless)
            : base(windowless ? new Guid().ToString() : "DFEAF541-F3E1-4C24-ACAC-99C30715084A", windowless)
        {
            _version = new Version(4, 0);
            _hostOptions = XcpHostOptions.Default;
            _baseUrl = DefaultBaseUri;
            
            _config = new Dictionary<string, object>(StringComparer.InvariantCultureIgnoreCase)
            {
                {"Background", "#FFFFFFFF"},
                {"Windowless", windowless},
                {"EnableGPUAcceleration", true},
                {"EnableNavigation", "all"},
                {"AllowHtmlPopupWindow", true},
                {"EnableHtmlAccess", true},
                {"MaxFrameRate", (uint)60},
                {"OnLoad", "OnLoad"},
                {"OnError", "OnError"},
                {"OnZoom", "OnZoom"},
                {"OnResize", "OnResize"},
                {"OnSourceDownloadComplete", "OnSourceDownloadComplete"},
                {"OnSourceDownloadProgressChanged", "OnSourceDownloadProgressChanged"},
            };
        }

        #region Public Properties

        /// <summary>
        /// Gets a value indicating whether the control has been created.
        /// </summary>
        public bool IsCreated
        {
            get { return _created; }
        }

        /// <summary>
        /// Gets a value indicating whether the control has been created. This property is
        /// identical to IsCreated.
        /// </summary>
        public new bool Created
        {
            get { return IsCreated; }
        }

        /// <summary>
        /// Gets or sets the URL that the hosted control should use as the base for any relative requests made for Source,
        /// to the downloader, etc.
        /// </summary>
        public Uri BaseUrl
        {
            get { return _baseUrl; }
            set
            {
                if (value == null) throw new ArgumentNullException("BaseUrl");
                if (!value.IsAbsoluteUri) throw new ArgumentException("BaseUrl must be an absolute Uri.", "BaseUrl");
                Uri source = Source;
                if (source != null)
                {
                    string scheme = value.Scheme.ToLower(); 
                    if (scheme == "http")
                    {
                        if (Uri.Compare(source, value, UriComponents.HostAndPort, UriFormat.UriEscaped, StringComparison.InvariantCultureIgnoreCase) != 0)
                        {
                            throw new ArgumentException("BaseUrl should have the same host name as the Source Uri.", "BaseUrl");
                        }
                    }
                }
                _baseUrl = value;
            }
        }

        /// <summary>
        /// Gets or sets the XcpHostOptions specified by the host.
        /// </summary>
        public XcpHostOptions HostOptions
        {
            get { return _hostOptions; }
            set { _hostOptions = value; }
        }

        /// <summary>
        /// Gets or sets the version of the intalled Silverlight plug-in.
        /// </summary>
        public Version Version
        {
            get { return _version; }
            set { _version = value; }
        }

        /// <summary>
        /// Gets or sets the URI specified for the source content (can be a XAML page or a package, depending on
        /// which programming model is used for the Silverlight content).
        /// </summary>
        public Uri Source
        {
            get
            {
                if (_xaml != null)
                {
                    return null;
                }
                else if (_unkControl == null)
                {
                    if (_config.ContainsKey("Source") && _config["Source"] != null)
                    {
                        string value = (string)_config["Source"];
                        return (value != null ? new Uri(value, UriKind.RelativeOrAbsolute) : null);
                    }
                    else return null;
                }
                else if (_unkControl is IXcpControl2)
                {
                    string value = (_unkControl as IXcpControl2).Source;
                    return (value != null ? new Uri(value, UriKind.RelativeOrAbsolute) : null);
                }
                else if (_unkControl is IXcpControl)
                {
                    string value = (_unkControl as IXcpControl).Source;
                    return (value != null ? new Uri(value, UriKind.RelativeOrAbsolute) : null);
                }
                else throw new InvalidActiveXStateException("Source", ActiveXInvokeKind.PropertyGet);
            }
            set
            {
                if (value != null && value.IsAbsoluteUri)
                {
                    string scheme = value.Scheme.ToLower();
                    if (scheme == "http")
                    {
                        _baseUrl = new Uri(value.GetComponents(UriComponents.SchemeAndServer, UriFormat.UriEscaped), UriKind.Absolute);
                    }
                    else if (scheme == "file")
                    {
                        _baseUrl = new Uri(Path.GetDirectoryName(value.LocalPath), UriKind.Absolute);
                    }
                    else
                    {
                        _baseUrl = value;
                    }
                }
                else
                {
                    _baseUrl = DefaultBaseUri;
                }
                if (_unkControl == null) _config["Source"] = (value != null ? value.ToString() : null);
                else if (_unkControl is IXcpControl2) (_unkControl as IXcpControl2).Source = (value != null ? value.ToString() : null);
                else if (_unkControl is IXcpControl) (_unkControl as IXcpControl).Source = (value != null ? value.ToString() : null);
                else throw new InvalidActiveXStateException("Source", ActiveXInvokeKind.PropertySet);

                _xaml = null;
            }
        }

        /// <summary>
        /// Gets or sets the XAML content dispayed in the Silverlight plug-in.
        /// </summary>
        public string Xaml
        {
            get
            {
                return _xaml;
            }
            set
            {
                if (value == string.Empty)
                {
                    throw new ArgumentException("The specified Xaml content is empty.");
                }

                string source = (value != null ? "#Xaml" : null);
                _baseUrl = DefaultBaseUri;

                if (_unkControl == null)
                {
                    _xaml = value;
                    _config["Source"] = source;
                }
                else
                {
#if !NO_XAML_SOURCE_FIX
                    // When the source of silverlight content switches from a IXcpControl2.Source to an explicit #Xaml source
                    // after the control is loaded, it seems to have trouble updating itself to render the new xaml. In these cases,
                    // we will create a temp file to hold the xaml and point silverlight to it.

                    Uri oldSource = this.Source;
                    if (value != null && (_unkControl is IXcpControl2 || _unkControl is IXcpControl))
                    {
                        try
                        {
                            if (_tempXamlFile == null)
                            {
                                _tempXamlFile = Path.GetTempFileName();
                            }
                            using (var writer = File.CreateText(_tempXamlFile))
                            {
                                writer.Write(value);
                            }

                            _xaml = value;
                            if (_unkControl is IXcpControl2) (_unkControl as IXcpControl2).Source = new Uri(_tempXamlFile, UriKind.Absolute).ToString();
                            else if (_unkControl is IXcpControl) (_unkControl as IXcpControl).Source = new Uri(_tempXamlFile, UriKind.Absolute).ToString();

                            return;
                        }
                        catch
                        {
                            Debug.Fail("Xaml source fix failed.");
                        }
                    }
#endif
                    _xaml = value;

                    if (_unkControl is IXcpControl2) (_unkControl as IXcpControl2).Source = source;
                    else if (_unkControl is IXcpControl) (_unkControl as IXcpControl).Source = source;
                    else throw new InvalidActiveXStateException("Source", ActiveXInvokeKind.PropertySet);
                }
            }
        }

        /// <summary>
        /// Gets a Boolean that declares whether the control's onLoad event has been raised.
        /// </summary>
        public bool IsLoaded
        {
            get
            {
                if (_unkControl == null) return false;
                if (_unkControl is IXcpControl2) return (_unkControl as IXcpControl2).IsLoaded;
                else if (_unkControl is IXcpControl) return (_unkControl as IXcpControl).IsLoaded;
                else throw new InvalidActiveXStateException("IsLoaded", ActiveXInvokeKind.PropertyGet);
            }
        }

        /// <summary>
        /// Gets the content sub-object.
        /// </summary>
        public Content Content
        {
            get
            {
                if (_content == null)
                {
                    _content = new Content(GetXcpControlContent(), _config, this);
                }
                return _content;
            }
        }

        /// <summary>
        /// Gets the settings sub-object.
        /// </summary>
        public Settings Settings
        {
            get
            {
                if (_settings == null)
                {
                    _settings = new Settings(GetXcpControlSettings(), _config, this);
                }
                return _settings;
            }
        }

        /// <summary>
        /// Gets or sets initialization parameters.
        /// </summary>
        public string InitParams
        {
            get
            {
                if (_unkControl == null)
                {
                    if (_config.ContainsKey("InitParams")) return (string)_config["InitParams"];
                    else return null;
                }
                else if (_unkControl is IXcpControl2) return (_unkControl as IXcpControl2).InitParams;
                else if (_unkControl is IXcpControl) return (_unkControl as IXcpControl).InitParams;
                else throw new InvalidActiveXStateException("InitParams", ActiveXInvokeKind.PropertyGet);
            }
            set
            {
                if (_unkControl == null) _config["InitParams"] = value;
                else if (_unkControl is IXcpControl2) (_unkControl as IXcpControl2).InitParams = value;
                else if (_unkControl is IXcpControl) (_unkControl as IXcpControl).InitParams = value;
                else throw new InvalidActiveXStateException("InitParams", ActiveXInvokeKind.PropertySet);
            }
        }

        /// <summary>
        /// Gets or sets the URI specified for the splash screen source XAML.
        /// </summary>
        public Uri SplashScreenSource
        {
            get
            {
                if (_unkControl == null)
                {
                    if (_config.ContainsKey("SplashScreenSource") && _config["SplashScreenSource"] != null)
                    {
                        string value = (string)_config["SplashScreenSource"];
                        return (value != null ? new Uri(value, UriKind.RelativeOrAbsolute) : null);
                    }
                    else return null;
                }
                else if (_unkControl is IXcpControl2)
                {
                    string value = (_unkControl as IXcpControl2).SplashScreenSource;
                    return (value != null ? new Uri(value, UriKind.RelativeOrAbsolute) : null);
                }
                else throw new InvalidActiveXStateException("SplashScreenSource", ActiveXInvokeKind.PropertyGet);
            }
            set
            {
                if (_unkControl == null) _config["SplashScreenSource"] = (value != null ? value.ToString() : null);
                else if (_unkControl is IXcpControl2) (_unkControl as IXcpControl2).SplashScreenSource = (value != null ? value.ToString() : null);
                else throw new InvalidActiveXStateException("SplashScreenSource", ActiveXInvokeKind.PropertySet);
            }
        }

        #endregion

        #region Protected Properties

        /// <summary>
        /// Gets the Silverlight plug-in host
        /// </summary>
        protected abstract ISilverlightHost Host { get; }

        ISilverlightHost IHostContainerServicesProvider.Host
        {
            get { return Host; }
        }

        /// <summary>
        /// Gets or sets the specified scripting handler method that will execute when the OnLoad event is raised by the control.
        /// </summary>
        protected object OnLoad
        {
            get
            {
                if (_unkControl is IXcpControl2) return (_unkControl as IXcpControl2).OnLoad;
                else if (_unkControl is IXcpControl) return (_unkControl as IXcpControl).OnLoad;
                else throw new InvalidActiveXStateException("get_OnLoad", ActiveXInvokeKind.MethodInvoke);
            }
            set
            {
                if (_unkControl is IXcpControl2) (_unkControl as IXcpControl2).OnLoad = value;
                else if (_unkControl is IXcpControl) (_unkControl as IXcpControl).OnLoad = value;
                else throw new InvalidActiveXStateException("set_OnLoad", ActiveXInvokeKind.MethodInvoke);
            }
        }

        /// <summary>
        /// Gets or sets the scripting handler method that can process any control-specific script level errors raised by the control.
        /// </summary>
        protected object OnError
        {
            get
            {
                if (_unkControl is IXcpControl2) return (_unkControl as IXcpControl2).OnError;
                else if (_unkControl is IXcpControl) return (_unkControl as IXcpControl).OnError;
                else throw new InvalidActiveXStateException("get_OnError", ActiveXInvokeKind.MethodInvoke);
            }
            set
            {
                if (_unkControl is IXcpControl2) (_unkControl as IXcpControl2).OnError = value;
                else if (_unkControl is IXcpControl) (_unkControl as IXcpControl).OnError = value;
                else throw new InvalidActiveXStateException("set_OnError", ActiveXInvokeKind.MethodInvoke);
            }
        }

        /// <summary>
        /// Gets or sets a scripting handler method that will execute when the onSourceDownloadComplete event is raised by the control.
        /// </summary>
        protected object _OnSourceDownloadComplete
        {
            get
            {
                if (_unkControl is IXcpControl2) return (_unkControl as IXcpControl2).OnSourceDownloadComplete;
                else throw new InvalidActiveXStateException("get_OnSourceDownloadComplete", ActiveXInvokeKind.MethodInvoke);
            }
            set
            {
                if (_unkControl is IXcpControl2) (_unkControl as IXcpControl2).OnSourceDownloadComplete = value;
                else throw new InvalidActiveXStateException("set_OnSourceDownloadComplete", ActiveXInvokeKind.MethodInvoke);
            }
        }

        /// <summary>
        /// Gets or sets the specified scripting handler method that will execute when the onSourceDownloadProgressChanged event is raised by the control.
        /// </summary>
        protected object _OnSourceDownloadProgressChanged
        {
            get
            {
                if (_unkControl is IXcpControl2) return (_unkControl as IXcpControl2).OnSourceDownloadProgressChanged;
                else throw new InvalidActiveXStateException("get_OnSourceDownloadProgressChanged", ActiveXInvokeKind.MethodInvoke);
            }
            set
            {
                if (_unkControl is IXcpControl2) (_unkControl as IXcpControl2).OnSourceDownloadProgressChanged = value;
                else throw new InvalidActiveXStateException("set_OnSourceDownloadProgressChanged", ActiveXInvokeKind.MethodInvoke);
            }
        }

        #endregion

        #region Private Properties

        private Uri DefaultBaseUri
        {
            get { return new Uri(Assembly.GetEntryAssembly().Location, UriKind.Absolute); }
        }
        
        #endregion

        #region Public Methods

        /// <summary>
        /// Called by the hosted control whenever a download is requested. Hosts should implement this method to provide
        /// specialized download behavior for cases where the download might request cross-domain URLs, or where the host
        /// has other restrictions on requests made as a URI.
        /// </summary>
        /// <param name="url">The URL to download.</param>
        /// <param name="callback">The downloader callback implementation.</param>
        /// <param name="stream">The stream containing the downloaded content.</param>
        /// <returns>Returns OK if the download was handled, Pending if it is being done asynchronously, or Default
        /// if Silverlight should handle the download.</returns>
        protected virtual DownloadResult DownloadUrl(string url, IDownloadUrlCallback callback, out Stream stream)
        {
            stream = null;

            Uri source = this.Source;
            if (source != null && Uri.Compare(source, new Uri(url, UriKind.RelativeOrAbsolute), UriComponents.HostAndPort, UriFormat.UriEscaped, StringComparison.InvariantCultureIgnoreCase) == 0)
            {
                return DownloadResult.Default;
            }
            else
            {
                ThreadPool.QueueUserWorkItem(delegate
                {
                    var client = new WebClient();

                    try
                    {
                        var data = client.DownloadData(url);
                        callback.NotifyDownloadSuceeded(new MemoryStream(data));
                    }
                    catch
                    {
                        callback.NotifyDownloadFailed();
                    }
                });

                return DownloadResult.Pending;
            }
        }

        /// <summary>
        /// Loads the CLR runtime
        /// </summary>
        public void LoadRuntime()
        {
            if (_unkControl is IXcpControl2) (_unkControl as IXcpControl2).LoadRuntime();
            else throw new InvalidActiveXStateException("LoadRuntime", ActiveXInvokeKind.MethodInvoke);
        }

        /// <summary>
        /// Creates a new instance of a Silverlight helper object.
        /// </summary>
        /// <param name="objectType">Specifies the type of object to create.</param>
        /// <returns>The resulting object.</returns>
        public SafeComObject<IXcpObject> CreateObject(string objectType)
        {
            if (objectType == null) throw new ArgumentNullException("objectType");
            if (objectType == string.Empty) throw new ArgumentException("Empty argument not allowed.", "objectType");

            IXcpObject xcpObject;
            if (_unkControl is IXcpControl2) xcpObject = (_unkControl as IXcpControl2).CreateObject(objectType);
            else if (_unkControl is IXcpControl) xcpObject = (_unkControl as IXcpControl).CreateObject(objectType);
            else throw new InvalidActiveXStateException("CreateObject", ActiveXInvokeKind.MethodInvoke);
            if (xcpObject != null) return new SafeComObject<IXcpObject>(xcpObject);
            else return null;
        }

        /// <summary>
        /// Determines whether the existing Silverlight plug-in
        /// is a matching version or is a compatible version with a specified version string.
        /// </summary>
        /// <param name="version">String specifying the version to check.</param>
        /// <returns>True if the version is supported; otherwise, False.</returns>
        public bool IsVersionSupported(string version)
        {
            if (version == null) throw new ArgumentNullException("version");
            if (version == string.Empty) throw new ArgumentException("Empty argument not allowed.", "version");
            
            if (_unkControl is IXcpControl2) return (_unkControl as IXcpControl2).IsVersionSupported(version);
            else if (_unkControl is IXcpControl) return (_unkControl as IXcpControl).IsVersionSupported(version);
            else throw new InvalidActiveXStateException("IsVersionSupported", ActiveXInvokeKind.MethodInvoke);
        }


        #endregion

        #region Protected Methods

        protected SafeComObject<object> GetXcpControlContent()
        {
            if (!_created) return null;
            if (_unkControl is IXcpControl2) return new SafeComObject<object>((_unkControl as IXcpControl2).Content);
            else if (_unkControl is IXcpControl) return new SafeComObject<object>((_unkControl as IXcpControl).Content);
            else return null;
        }

        protected SafeComObject<object> GetXcpControlSettings()
        {
            if (!_created) return null;
            if (_unkControl is IXcpControl2) return new SafeComObject<object>((_unkControl as IXcpControl2).Settings);
            else if (_unkControl is IXcpControl) return new SafeComObject<object>((_unkControl as IXcpControl).Settings);
            else return null;
        }

        protected new virtual object GetOcx()
        {
            return base.GetOcx();
        }

        protected override void OnHandleCreated(System.EventArgs e)
        {
            base.OnHandleCreated(e);

            if (base.IsHandleCreated)
            {
                _unkControl = this.GetOcx();

                uint miscStatus;
                if (_unkControl is IOleObject)
                {
                    IOleObject oleObject = (IOleObject)_unkControl;
                    oleObject.GetMiscStatus((uint)DVASPECT.DVASPECT_CONTENT, out miscStatus);
                    if ((miscStatus & (uint)OLEMISC.SETCLIENTSITEFIRST) > 0)
                    {
                        oleObject.SetClientSite(this);
                    }

                    if (oleObject is IPersistPropertyBag)
                    {
                        IPersistPropertyBag propertyBag = (IPersistPropertyBag)oleObject;
                        propertyBag.Load(this, null);
                    }

                    if ((miscStatus & (uint)OLEMISC.SETCLIENTSITEFIRST) == 0)
                    {
                        oleObject.SetClientSite(this);
                    }

                    oleObject.SetHostNames("SilverlightHost", null);
                }
            }
        }

        protected override void OnCreateControl()
        {
            base.OnCreateControl();

            if (base.IsHandleCreated)
            {
                _created = true;

                if (_settings != null && _settings._comObject == null)
                {
                    var settings = GetXcpControlSettings();
                    if (settings != null)
                    {
                        _settings._comObject = settings;
                    }
                    else
                    {
                        throw new InvalidActiveXStateException("Settings", ActiveXInvokeKind.PropertyGet);
                    }
                }

                if (_content != null && _content._comObject == null)
                {
                    var content = GetXcpControlContent();
                    if (content != null)
                    {
                        _content._comObject = content;
                    }
                    else
                    {
                        throw new InvalidActiveXStateException("Content", ActiveXInvokeKind.PropertyGet);
                    }
                }
            }
        }

        protected bool InvokeBasePreProcessMessage(ref Message msg)
        {
            return base.PreProcessMessage(ref msg);
        }

        public override bool PreProcessMessage(ref Message msg)
        {
            return false;
        }

        protected override void Dispose(bool disposing)
        {
            if (_settings != null && _settings._comObject != null)
            {
                _settings._comObject.Dispose();
            }
            if (_content != null && _content._comObject != null)
            {
                _content._comObject.Dispose();
            }
#if !NO_XAML_SOURCE_FIX
            if (_tempXamlFile != null)
            {
                if (File.Exists(_tempXamlFile))
                {
                    try { File.Delete(_tempXamlFile); }
                    catch { }
                }
            }
            _tempXamlFile = null;
#endif

            _settings = null;
            _content = null;
            _config = null;
            _unkControl = null;

            base.Dispose(disposing);
        }

        /// <summary>
        /// Called when the Silverlight control is created.
        /// </summary>
        protected virtual void OnCreated(EventArgs e)
        {
        }

        /// <summary>
        /// Called whenever the XAML DOM has successfully loaded.
        /// </summary>
        protected virtual void OnSilverlightLoaded(EventArgs e)
        {
        }

        /// <summary>
        /// Called whenever errors are raised from within the XAML DOM of the hosted control.
        /// </summary>
        /// <param name="e">The details of the error.</param>
        protected virtual void OnSilverlightError(SilverlightErrorEventArgs e)
        {
        }

        /// <summary>
        /// Invoked whenever events occur within the object model of the hosted control.
        /// </summary>
        /// <param name="e">The details of the event.</param>
        protected virtual void OnSilverlightEvent(SilverlightEventArgs e)
        {
        }

        /// <summary>
        /// Called when the source download has finished.
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnSourceDownloadComplete(EventArgs e)
        {
        }

        /// <summary>
        /// Called when the source download progress changes.
        /// </summary>
        /// <param name="e">The details of the download progress.</param>
        protected virtual void OnSourceDownloadProgressChanged(SourceDownloadProgressChangedEventArgs e)
        {
        }

        /// <summary>
        /// Called after the content in the Silverlight plug-in has completely loaded.
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnContentLoaded(EventArgs e)
        {
            Content.OnLoaded(this, e);
        }

        /// <summary>
        /// Called when when the Silverlight plug-in generates a XAML parse error or
        /// run-time error at the native-code level.
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnContentError(ErrorEventArgs e)
        {
            Content.OnError(this, e);
        }

        /// <summary>
        /// Called when the Silverlight plug-in content area receives a host-generated zoom event.
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnContentZoomed(EventArgs e)
        {
            Content.OnZoomed(this, e);
        }

        /// <summary>
        /// Called when the ActualHeight or ActualWidth of the Silverlight plug-in change.
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnContentResized(EventArgs e)
        {
            Content.OnResized(this, e);
        }

        /// <summary>
        /// Called whenever the FullScreen property of the Silverlight plug-in changes.
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnFullScreenChanged(EventArgs e)
        {
            Content.OnFullScreenChanged(this, e);
        }

        #endregion

        #region IXcpControlHost Members

        XcpHostOptions IXcpControlHost.GetHostOptions()
        {
            return _hostOptions;
        }

        void IXcpControlHost.NotifyLoaded()
        {
            OnSilverlightLoaded(new EventArgs());
        }

        void IXcpControlHost.NotifyError(string error, string source, int line, int column)
        {
            OnSilverlightError(new SilverlightErrorEventArgs()
            {
                Error = error,
                SourceFile = source,
                Line = line,
                Column = column
            });
        }

        object IXcpControlHost.InvokeHandler(string name, object param1, object param2)
        {
            switch (name)
            {
                case "OnLoad":
                    {
                        OnContentLoaded(new EventArgs());
                        break;
                    }
                case "OnError":
                    {
                        ErrorEventArgs args;
                        ErrorType errorType;
                        try
                        {
                            errorType = (ErrorType)Enum.Parse(typeof(ErrorType), (string)param1.GetType().InvokeMember("ErrorType", BindingFlags.GetProperty, null, param1, null));
                        }
                        catch
                        {
                            errorType = ErrorType.UnknownError;
                        }

                        switch (errorType)
                        {
                            case ErrorType.ParserError:
                                {
                                    var parserErrorArgs = new ParserErrorEventArgs();
                                    parserErrorArgs.CharPosition = (int)param1.GetType().InvokeMember("CharPosition", BindingFlags.GetProperty, null, param1, null);
                                    parserErrorArgs.LineNumber = (int)param1.GetType().InvokeMember("LineNumber", BindingFlags.GetProperty, null, param1, null);
                                    parserErrorArgs.XamlFile = (string)param1.GetType().InvokeMember("XamlFile", BindingFlags.GetProperty, null, param1, null);
                                    parserErrorArgs.XmlAttribute = (string)param1.GetType().InvokeMember("XmlAttribute", BindingFlags.GetProperty, null, param1, null);
                                    parserErrorArgs.XmlElement = (string)param1.GetType().InvokeMember("XmlElement", BindingFlags.GetProperty, null, param1, null);

                                    args = parserErrorArgs;
                                }
                                break;
                            case ErrorType.RuntimeError:
                                {
                                    var runtimeErrorArgs = new RuntimeErrorEventArgs();
                                    runtimeErrorArgs.CharPosition = (int)param1.GetType().InvokeMember("CharPosition", BindingFlags.GetProperty, null, param1, null);
                                    runtimeErrorArgs.LineNumber = (int)param1.GetType().InvokeMember("LineNumber", BindingFlags.GetProperty, null, param1, null);
                                    runtimeErrorArgs.MethodName = (string)param1.GetType().InvokeMember("MethodName", BindingFlags.GetProperty, null, param1, null);

                                    args = runtimeErrorArgs;
                                }
                                break;
                            default:
                                args = new ErrorEventArgs();
                                break;
                        }

                        args.ErrorType = errorType;
                        args.ErrorCode = (int)param1.GetType().InvokeMember("ErrorCode", BindingFlags.GetProperty, null, param1, null);
                        args.ErrorMessage = (string)param1.GetType().InvokeMember("ErrorMessage", BindingFlags.GetProperty, null, param1, null);
                        args.Name = (string)param1.GetType().InvokeMember("Name", BindingFlags.GetProperty, null, param1, null);

                        OnContentError(args as ErrorEventArgs);
                        break;
                    }
                case "OnZoom":
                    {
                        OnContentZoomed(new EventArgs());
                        break;
                    }
                case "OnResize":
                    {
                        OnContentResized(new EventArgs());
                        break;
                    }
                case "OnFullScreenChanged":
                    {
                        OnFullScreenChanged(new EventArgs());
                        break;
                    }
                case "OnSourceDownloadComplete":
                    {
                        OnSourceDownloadComplete(new EventArgs());
                        break;
                    }
                case "OnSourceDownloadProgressChanged":
                    {
                        var args = new SourceDownloadProgressChangedEventArgs()
                        {
                            Progress = (float)param1.GetType().InvokeMember("Progress", BindingFlags.GetProperty, null, param1, null)
                        };
                        OnSourceDownloadProgressChanged(args as SourceDownloadProgressChangedEventArgs);
                        break;
                    }
                default:
                    {
                        var eventArgs = new SilverlightEventArgs()
                        {
                            Param1 = param1,
                            Param2 = param2
                        };
                        OnSilverlightEvent(eventArgs as SilverlightEventArgs);
                        return (eventArgs as SilverlightEventArgs).Result;
                    }
            }

            return null;
        }

        string IXcpControlHost.GetBaseUrl()
        {
            return _baseUrl.ToString();
        }

        string IXcpControlHost.GetNamedSource(string sourceName)
        {
            if (sourceName == "Xaml")
            {
                return _xaml;
            }
            else throw new ArgumentException("sourceName");
        }

        int IXcpControlHost.DownloadUrl(string url, IXcpControlDownloadCallback callback, out IStream stream)
        {
            try
            {
                stream = null;
                Stream ioStream = null;
                DownloadResult result = DownloadUrl(url, new DownloadUrlCallback(this, new SafeComObject<IXcpControlDownloadCallback>(callback)), out ioStream);
                switch (result)
                {
                    case DownloadResult.OK:
                        return HRESULT.S_OK;
                    case DownloadResult.Pending:
                        return HRESULT.E_PENDING;
                    default:
                    case DownloadResult.Default:
                        return HRESULT.S_FALSE;
                }
            }
            catch (Exception e)
            {
                stream = null;
                return Marshal.GetHRForException(e);
            }
        }

        #endregion

        #region IXcpControlHost2 Members

        XcpHostOptions IXcpControlHost2.GetHostOptions()
        {
            return (this as IXcpControlHost).GetHostOptions();
        }

        void IXcpControlHost2.NotifyLoaded()
        {
            (this as IXcpControlHost).NotifyLoaded();
        }

        void IXcpControlHost2.NotifyError(string error, string source, int line, int column)
        {
            (this as IXcpControlHost).NotifyError(error, source, line, column);
        }

        object IXcpControlHost2.InvokeHandler(string name, object param1, object param2)
        {
            return (this as IXcpControlHost).InvokeHandler(name, param1, param2);
        }

        string IXcpControlHost2.GetBaseUrl()
        {
            return (this as IXcpControlHost).GetBaseUrl();
        }

        string IXcpControlHost2.GetNamedSource(string sourceName)
        {
            return (this as IXcpControlHost).GetNamedSource(sourceName);
        }

        int IXcpControlHost2.DownloadUrl(string url, IXcpControlDownloadCallback callback, out IStream stream)
        {
            return (this as IXcpControlHost).DownloadUrl(url, callback, out stream);
        }

        void IXcpControlHost2.GetCustomAppDomain(out object ppAppDomain)
        {
            throw new NotImplementedException();
        }

        void IXcpControlHost2.GetControlVersion(ref uint majorVersion, ref uint minorVersion)
        {
            if (_version == null)
            {
                _version = new Version((int)majorVersion, (int)minorVersion);
            }
            else
            {
                majorVersion = (uint)_version.Major;
                minorVersion = (uint)_version.Minor;
            }
        }

        #endregion

        #region IXcpControlHost3 Members

        XcpHostOptions IXcpControlHost3.GetHostOptions()
        {
            return (this as IXcpControlHost).GetHostOptions();
        }

        void IXcpControlHost3.NotifyLoaded()
        {
            (this as IXcpControlHost).NotifyLoaded();
        }

        void IXcpControlHost3.NotifyError(string error, string source, int line, int column)
        {
            (this as IXcpControlHost).NotifyError(error, source, line, column);
        }

        object IXcpControlHost3.InvokeHandler(string name, object param1, object param2)
        {
            return (this as IXcpControlHost).InvokeHandler(name, param1, param2);
        }

        string IXcpControlHost3.GetBaseUrl()
        {
            return (this as IXcpControlHost).GetBaseUrl();
        }

        string IXcpControlHost3.GetNamedSource(string sourceName)
        {
            return (this as IXcpControlHost).GetNamedSource(sourceName);
        }

        int IXcpControlHost3.DownloadUrl(string url, IXcpControlDownloadCallback callback, out IStream stream)
        {
            return (this as IXcpControlHost).DownloadUrl(url, callback, out stream);
        }

        void IXcpControlHost3.GetCustomAppDomain(out object appDomain)
        {
            (this as IXcpControlHost2).GetCustomAppDomain(out appDomain);
        }

        void IXcpControlHost3.GetControlVersion(ref uint majorVersion, ref uint minorVersion)
        {
            (this as IXcpControlHost2).GetControlVersion(ref majorVersion, ref minorVersion);
        }

        string IXcpControlHost3.GetDefaultThemeXaml(string assemblyName)
        {
            return null;
        }

        string IXcpControlHost3.GetDefaultPortableUserInterfaceFontInfo()
        {
            return null;
        }

        int IXcpControlHost3.GetComAutomationObject(string progId, XcpHost_GetComAutomationObjectFlags flags, out object automationObject)
        {
            automationObject = null;
            return HRESULT.S_FALSE;
        }

        #endregion

        #region IServiceProvider Members

        int Interop.IServiceProvider.QueryService(ref Guid guidService, ref Guid riid, out IntPtr ppvObject)
        {
            ppvObject = IntPtr.Zero;

            if (guidService == typeof(IXcpControlHost).GUID && riid == typeof(IXcpControlHost).GUID)
            {
                ppvObject = Marshal.GetComInterfaceForObject(this, typeof(IXcpControlHost));
                return HRESULT.S_OK;
            }

            if (guidService == typeof(IXcpControlHost2).GUID && riid == typeof(IXcpControlHost2).GUID)
            {
                ppvObject = Marshal.GetComInterfaceForObject(this, typeof(IXcpControlHost2));
                return HRESULT.S_OK;
            }

            if (guidService == typeof(IXcpControlHost3).GUID && riid == typeof(IXcpControlHost3).GUID)
            {
                ppvObject = Marshal.GetComInterfaceForObject(this, typeof(IXcpControlHost3));
                return HRESULT.S_OK;
            }

            return HRESULT.E_NOINTERFACE;
        }

        #endregion

        #region IPropertyBag Members

        int IPropertyBag.Read(string propName, out object value, IErrorLog errorLog)
        {
            if (_config.ContainsKey(propName))
            {
                value = _config[propName];
                return HRESULT.S_OK;
            }
            else
            {
                value = null;
                return HRESULT.E_INVALIDARG;
            }
        }

        int IPropertyBag.Write(string propName, ref object value)
        {
            return HRESULT.E_NOTIMPL;
        }

        #endregion

        #region IOleClientSite Members

        int IOleClientSite.SaveObject()
        {
            return HRESULT.E_NOTIMPL;
        }

        int IOleClientSite.GetMoniker(uint dwAssign, uint dwWhichMoniker, out System.Runtime.InteropServices.ComTypes.IMoniker ppmk)
        {
            ppmk = null;
            return HRESULT.E_NOTIMPL;
        }

        int IOleClientSite.GetContainer(out IOleContainer ppContainer)
        {
            ppContainer = null;
            return HRESULT.E_NOTIMPL;
        }

        int IOleClientSite.ShowObject()
        {
            return HRESULT.S_OK;
        }

        int IOleClientSite.OnShowWindow(int fShow)
        {
            return HRESULT.S_OK;
        }

        int IOleClientSite.RequestNewObjectLayout()
        {
            return HRESULT.S_OK;
        }

        #endregion
    }
}
